﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SGS.Standard;
using System.Net.Sockets;
using System.Net;
using SGS;
using SGSGameServer.Protocal;
using SGSGameServer.DataStruct;

namespace SGSGameServer
{
    public class ServerContext
    {
        private Socket _ListenerSocket = null;        

        public ServerContext()
        {
        }

        public void Start()
        {
            _ListenerSocket = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
            _ListenerSocket.Bind( new IPEndPoint( IPAddress.Any, 6000 ) );
            _ListenerSocket.Listen( 100 );
            _ListenerSocket.BeginAccept( OnConnectRequest, _ListenerSocket );
        }

        public void Stop()
        {
        }

        private void OnConnectRequest( IAsyncResult ar )
        {
            Socket listener = (Socket)ar.AsyncState;
            Socket newSocket = listener.EndAccept( ar );

            // 继续异步监听，等待下一次连接
            listener.BeginAccept( new AsyncCallback( OnConnectRequest ), listener );

            // 处理新链接的信息
            OnNewConnection( newSocket );
        }

        public void OnNewConnection( Socket clientSocket )
        {
            // 发送欢迎信息和服务器版本信息
            Console.WriteLine( clientSocket.RemoteEndPoint.ToString() + "join server" );

            SocketHelper.Send( clientSocket, "MySanGuoSha Game Server" );
            SocketHelper.Send( clientSocket, new ServerInfo()
            { 
                ServerVersion = this.GetType().Assembly.GetName().Version.ToString() ,
                CoreVersion = typeof( GamePool ).Assembly.GetName().Version.ToString()
            } );

            // 等待客户端认证信息
            SocketHelper.Send<Message<string>>( clientSocket, new Message<string>( 0x00000001, "Wait authenticate data..." ) );
            NetMessage<Message<GameAuthorization>> authenticateMsg = SocketHelper.Receive<Message<GameAuthorization>>( clientSocket );

            if (authenticateMsg.ContentObject.MessageType != 0x00000002)
            {
                SocketHelper.Send( clientSocket, "Error Protocal" );
                clientSocket.Close();
                clientSocket.Dispose();
            }
            else
            {
                var socketInfo = SocketPool.Instance.AddSocket( clientSocket );
                socketInfo.AuthorizationUserName = authenticateMsg.ContentObject.Data.UserName;
            }

            // 接收到客户端数据时回调函数
            //SetupRecieveCallback( clientSocket );
            
        }

        public void SetupRecieveCallback( Socket socket )
        {
            AsyncCallback recieveData = new AsyncCallback( OnRecievedData );//收到数据时回调数据处理函数
            SocketError socketerror = SocketError.Success;

            var socketInfo = SocketPool.Instance.GetSocketInfo( socket );

            // 把接收到的数据预先分配给服务端byte[]来储存
            socket.BeginReceive( socketInfo.DataBuffer, 0, socketInfo.DataBuffer.Length, SocketFlags.None, out socketerror, recieveData, socketInfo );

            // 处理接收数据时各种异常
            CatchWithSocketError( socketerror, socket );
        }

        public void OnRecievedData( IAsyncResult ar )
        {
            var socketInfo = (SGSGameServer.SocketPool.SocketInfo)ar.AsyncState;
            // listenerMess.Socket = sock;

            SocketError socketerror = SocketError.Success;
            int nBytesRec = socketInfo.Socket.EndReceive( ar, out socketerror );

            CatchWithSocketError( socketerror, socketInfo.Socket );
            if (nBytesRec > 0)
            {
                listenerMess.Stream.Write( listenerMess.Buffer, 0, nBytesRec );//把服务端byte[]中接收到的数据交给Stream类的write方法,写入到Stream类的buffer byte[]中。
                //...对接收到的数据进行分析处理
                SetupRecieveCallback( socket );//继续异步等待客户端发送过来的数据
            }
            else
            {
                socket.Shutdown( SocketShutdown.Both );
                socket.Close();
            }
        }

        /// <summary>
        /// 根据错误号作出处理
        /// </summary>
        /// <param name="error"></param>
        /// <param name="client"></param>
        private void CatchWithSocketError( SocketError error, Socket socket )
        {
            #region 各种错误号
            //AccessDenied已试图通过被其访问权限禁止的方式访问 Socket。 
            //ConnectionAborted此连接由 .NET Framework 或基础套接字提供程序中止。 
            // Disconnecting正常关机正在进行中。
            //Fault 基础套接字提供程序检测到无效的指针地址。  
            //HostDown 由于远程主机被关闭，操作失败。  
            //HostNotFound 无法识别这种主机。该名称不是正式的主机名或别名。  
            //HostUnreachable 没有到指定主机的网络路由。  
            //InProgress 阻止操作正在进行中。  
            //Interrupted 已取消阻止 Socket 调用的操作。  
            //InvalidArgument 给 Socket 成员提供了一个无效参数。  
            //IOPending 应用程序已启动一个无法立即完成的重叠操作。  
            //IsConnected Socket 已连接。  
            //MessageSize 数据报太长。  
            //NetworkDown 网络不可用。  
            //NetworkReset 应用程序试图在已超时的连接上设置 KeepAlive。  
            //NetworkUnreachable 不存在到远程主机的路由。  
            //NoBufferSpaceAvailable 没有可用于 Socket 操作的可用缓冲区空间。  
            //NoData 在名称服务器上找不到请求的名称或 IP 地址。  
            //NoRecovery 错误不可恢复或找不到请求的数据库。  
            //NotConnected 应用程序试图发送或接收数据，但是 Socket 未连接。  
            //NotInitialized 尚未初始化基础套接字提供程序。  
            //NotSocket 对非套接字尝试 Socket 操作。  
            //OperationAborted 由于 Socket 已关闭，重叠的操作被中止。  
            //OperationNotSupported 协议族不支持地址族。  
            //ProcessLimit 正在使用基础套接字提供程序的进程过多。  
            //ProtocolFamilyNotSupported 未实现或未配置协议族。  
            //ProtocolNotSupported 未实现或未配置协议。  
            //ProtocolOption 对 Socket 使用了未知、无效或不受支持的选项或级别。  
            //ProtocolType 此 Socket 的协议类型不正确。  
            //Shutdown 发送或接收数据的请求未得到允许，因为 Socket 已被关闭。  
            //SocketError 发生了未指定的 Socket 错误。  
            //SocketNotSupported 在此地址族中不存在对指定的套接字类型的支持。  
            //Success Socket 操作成功。  
            //SystemNotReady 网络子系统不可用。  
            //TimedOut 连接尝试超时，或者连接的主机没有响应。  
            //TooManyOpenSockets 基础套接字提供程序中打开的套接字太多。  
            //TryAgain 无法解析主机名。请稍后重试。  
            //TypeNotFound 未找到指定的类。  
            //VersionNotSupported 基础套接字提供程序的版本超出范围。  
            //WouldBlock 对非阻止性套接字的操作不能立即完成。 
            #endregion

            if (error == SocketError.Disconnecting || error == SocketError.Fault || error == SocketError.IsConnected || error == SocketError.SocketError)
            {
                // Console.WriteLine( "{0} error:{1}", socket.RemoteEndPoint.ToString(), error );
                socket.Close();
                // ui.Post( this.uiDisPlay.delUser, client );
            }
            if (error == SocketError.MessageSize || error == SocketError.NotConnected || error == SocketError.ProcessLimit || error == SocketError.TooManyOpenSockets)
            {
                // Console.WriteLine( "{0} error:{1}", socket.RemoteEndPoint.ToString(), error );
                socket.Close();
                // ui.Post( this.uiDisPlay.delUser, client );
            }

            if (error != SocketError.Success)
            {
                Console.WriteLine( "{0} error:{1}", socket.RemoteEndPoint.ToString(), error );
            }
        }
    }
}
